AWS入門ブログリレー2024〜Amazon DynamoDB編〜
当エントリは弊社AWS事業本部による『AWS 入門ブログリレー 2024』の37日目のエントリです。
このブログリレーの企画は、普段 AWS サービスについて最新のネタ・深い/細かいテーマを主に書き連ねてきたメンバーの手によって、 今一度初心に返って、基本的な部分を見つめ直してみよう、解説してみようというコンセプトが含まれています。
AWS をこれから学ぼう!という方にとっては文字通りの入門記事として、またすでに AWS を活用されている方にとっても AWS サービスの再発見や 2024 年のサービスアップデートのキャッチアップの場となればと考えておりますので、ぜひ最後までお付合い頂ければ幸いです。
では、さっそくいってみましょう。今回のテーマは『Amazon DynamoDB』です。
DynamoDBの特徴
Amazon DynamoDBはAWSが提供するフルマネージドなNoSQLデータベースサービスです。主な特徴は以下のとおりです。
- キーバリューストアまたはワイドカラムデータモデルを採用しており、従来のリレーショナルデータベースとは異なる設計思想
- パフォーマンス劣化のない無限のスケーリングが可能
- HTTPリクエストによる接続モデルを採用しており、永続的なTCP接続は不要
- AWSのIAMによる認証・認可
- IaCフレンドリーなデータベース
- 読み取り/書き込みのスループットに応じた柔軟な課金モデル
- ストリーミング機能によるデータ変更のキャプチャが可能
- AWS側で完全にマネージドされているため、運用作業が不要
この入門ブログでは、DynamoDBの基本概念から最新のアップデート、ベストプラクティスまでを解説します。
DynamoDBの基礎
キーバリューストアおよびワイドカラムストアとしての特徴
DynamoDBはキーバリューストアとしても、ワイドカラムストアとしても使用できます。
- キーバリューストア:
- 分散ハッシュテーブルのように機能する
- 各アイテムは、ユニークなキーで識別される
- キーを指定して1件のデータを直接取得する
- 単純な構造で高速なデータアクセスが可能
- 1度に1件のデータしか取得できないという制約がある
- ワイドカラムストア:
- キーバリューストアを発展させた形式
- 1つのキーに対して複数の属性(カラム)を関連付けられる
- カラムはソート済みのデータ構造(B-Treeなど)で管理されている
- キーとカラムの範囲指定で、複数のデータを効率的に取得できる
テーブル、アイテム、属性の概念
DynamoDBを構成する基本的なデータ要素の定義とその関係性について説明します。
(引用元:Choosing the Right DynamoDB Partition Key)
- テーブル:
- DynamoDB内において、データを格納する主要な構造体
- リレーショナルデータベースのテーブルと似ていますが、スキーマが固定されているわけではなく、異なるアイテムで異なる属性を持つことが可能
- アイテム:
- テーブル内の各個別のデータエントリーであり、リレーショナルデータベースの行(row)に相当
- アイテムは一つ以上の属性を持ち、それぞれが重複しないプライマリーキーによって一意に識別される
- 属性(Attribute):
- アイテムの細かいデータの断片で、リレーショナルデータベースのカラム(column)に相当
- 属性にはキー(名前)とバリュー(値)があり、アイテムごとに異なる属性の型を持つことが可能
- 属性は様々なデータ型をサポートしており、アプリケーションが必要とするデータを柔軟に保存できる
プライマリキーとセカンダリインデックスについて
DynamoDBにおけるデータの一意性と効率的なアクセスを実現するためのキー設計の原理を理解しておきましょう。 プライマリキーがデータの整理と高速アクセスを実現するための基盤であるため、適切なキーを選択することがDynamoDBを使用する際のパフォーマンスとコスト効率に大きく影響を与えることになります。
- プライマリキー:
- プライマリキーはDynamoDBのテーブル内で各アイテムを一意に識別するために使用される
- 単一のパーティションキーのみを使用する「単純プライマリキー」、パーティションキーとソートキーの組み合わせを使用する「複合プライマリキー」がある
- プライマリキーの選定とデザインがDynamoDBのデータモデリングで最も重要
- 効率的なデータアクセスと整合性を保つために必要な概念
- セカンダリインデックス:
- プライマリキー以外の属性に対する代替キーを提供
- プライマリキーでは実現できないデータアクセスパターンを可能にする
- セカンダリインデックス作成時に、インデックスのプライマリキーを指定
- メインテーブルのデータがインデックスにコピーされ、インデックスに対してクエリが実行できる
プライマリキーとセカンダリインデックスを適切に設計することが、DynamoDBでのデータモデリングの肝となります。プライマリキーはデータアクセスの基点となり、セカンダリインデックスはプライマリキーでは実現できないアクセスパターンを補完する役割を果たします。
その他の用語についても整理しておきます。
- 単純プライマリキー:
- 単一のパーティションキーのみを使用する
- 1件ずつのアイテムにのみアクセスできる
- 複合プライマリキー:
- パーティションキーとソートキー、2つの要素から構成される
- パーティションキーが同じ複数のアイテムを一括で取得できる
- パーティションキー:
- 以前はハッシュキーと呼ばれていたもの
- パーティションキー(単一の属性から成るハッシュキー)は、アイテムが分散される基礎となる
- パーティションキーの値は内部的にハッシュ関数を通して、特定のパーティションにマッピングされる
- ソートキー:
- 以前はレンジキーと呼ばれていたもの
- オプションでソートキーを追加して、プライマリキーを複合キー(パーティションキーとソートキーの組み合わせ)にすることができる
- 同一パーティションキー内でソートキーにより、アイテムを順序付け、効率的なクエリを可能にする
セカンダリインデックスの種類
セカンダリインデックスを作成する際に、インデックスのスキーマを指定する必要があります。キースキーマはテーブルのプライマリキーのように、セカンダリインデックスのパーティションキーまたはパーティションキー+ソートキーを指定します。
セカンダリインデックスには以下の2種類があります。
キースキーマ | 作成のタイミング | 整合性 | |
---|---|---|---|
ローカルセカンダリインデックス(LSI) | テーブルと同じパーティションキーを使用(ソートキーが異なるインデックス) | テーブル作成時にあらかじめ定義が必要 | 結果整合性および協力な整合性のある読み取りが可能(コストは高くなります) |
グローバルセカンダリインデックス(GSI) | テーブルの任意の属性をパーティションキーおよびソートキーとして使用可能 | テーブル作成後でも追加可能 | 結果整合性のある読み取りのみ |
一般的には、より柔軟なグローバルセカンダリインデックスを選択する場面が多いですが、整合性に関して厳しい要件があるようなケースではローカルセカンダリインデックスを検討します。その他にも保持できるインデックスの数、データサイズの制限、スループットの消費等の細かな機能差については公式ドキュメントを参照ください。
DynamoDBの主要な機能と概念
DynamoDB Streams
DynamoDB Streamsは、DynamoDBテーブル内のアイテムに対する変更(作成、更新、削除)をキャプチャし、その変更ログをストリームとして出力する機能です。
このストリームデータを、AWS LambdaやKinesis、その他のコンピューティングリソースで非同期に処理することができます。つまり、DynamoDBの変更イベントをリアルタイムで拾い、別のシステムに伝搬したり、バッチ処理を行ったりできます。
DynamoDB Streamsを活用することで、以下のようなユースケースが実現できます。
- DynamoDBをワークキューとして使う
- マイクロサービス間でイベントを伝搬する
- データウェアハウスへの変更ログ出力
- 監査ログの収集
DynamoDB StreamsとAWS Lambdaを組み合わせれば、フルマネージドな環境でデータベース変更イベントに対する処理を行えます。このようにDynamoDB Streamsは、イベントドリブンアーキテクチャを実現する上で重要な機能となっています。データベースの変更ログをリアルタイムにキャプチャでき、様々な活用が可能になります。
Time to Live(TTL)
TTLはDynamoDBのアイテムに対して有効期限を設定し、その期限を過ぎたアイテムを自動的に削除する機能です。
TTLを使うには、DynamoDBテーブルの属性の1つをTTL属性として指定します。そして、削除したいアイテムに対して、その属性にUnixタイムスタンプ形式の有効期限を設定します。DynamoDBは定期的にテーブルを確認し、TTL属性の値が現在時刻を過ぎているアイテムを自動的に削除します。
TTLの利点は、以下のようなことが挙げられます。
- 短期間のデータ保存に適している
- 手動での削除ジョブが不要になる
- テーブル内のすべてのアイテムにTTLを設定する必要はない
- アイテムごとに異なる有効期限を設定できる
ただし、TTLを使う際は注意点もあります。DynamoDBは「TTLを過ぎた時間から48時間以内に削除される」としか述べられておらず、即時削除を保証するものではありません。アプリケーションのデータの正確性をTTLに依存させるのではなく、アプリケーション側でアイテムの有効期限をチェックする必要があります。TTLは自動削除の補助的な役割に過ぎません。
このようにTTLを活用することで、一時的なデータの格納と自動削除を実現でき、DynamoDBの運用コストを削減できます。ただし、アプリケーションロジックとの連携が重要になります。
パーティション
パーティションは、DynamoDBテーブルの基本的なストレージユニットです。DynamoDBはデータを複数のサーバーインスタンス(パーティション)に分散(シャード)して格納することで、無限のスケーリングを実現しています。
リクエストが来ると、リクエストルーターがパーティションキーにハッシュ関数を適用し、そのデータを格納するサーバーを特定します。このデザインにより、データ量に応じて無制限にストレージノードを追加できます。
(引用元:パーティションとデータ分散)
整合性モデル
DynamoDBでは、データの整合性レベルとして「強い整合性」と「結果整合性」の2つのオプションがあります。
DynamoDBの書き込みフロー
読み取りの整合性について説明する前段として、まずDynamoDBの書き込みの仕組みを理解しておくと良いでしょう。DynamoDBにデータを書き込む際、すべてのリクエストはリクエストルータに送られます。リクエストルータで書き込みに対するアクセス権の認証が行われ、アイテムのパーティションキーをハッシュ化し、適切なプライマリーノードへ送信します。
DynamoDBのデータは自動的に3つのAZに複製されますが、プライマリーノード1つとセカンダリノード2つで構成されています。プライマリーノードは送られてきた書き込みをコミットした後、1つのセカンダリノードへコピーを書き込みを行います。セカンダリノード1の書き込み完了を以て、クライアントには書き込み完了のレスポンスを返します。この時点でセカンダリノード2への複製は行われていないことに注目してください。
クライアントへの書き込み完了を返した後、プライマリノードは非同期でセカンダリノード2へのレプリケートを行います。ざっくりとした説明ではありますが、これがDynamoDBの書き込みの仕組みとなっています。
DynamoDBの読み取り
先述のとおりプライマリノード(ストレージノードリーダー)のデータは最新であることが保証されています。「強い整合性」は必ずプライマリーノードのデータを参照します。
- 強い整合性:
- 読み取り時に、その時点までのすべての書き込みが反映された状態を返す
- 読み取りリクエストは必ずプライマリノードを参照する
- 整合性は保たれるが、コストが高い
(引用元:Amazon DynamoDB How it works)
一方、結果整合性は3つに分散されたストレージノードのいずれかにアクセスしますので、タイミングによっては非同期レプリケートの完了していないセカンダリノードから古いデータを取得する場合があります。ただし、ノードへの負荷を分散できるため、読み取りコストは「強い整合性」の半分にデザインされています。
- 結果整合性:
- 読み取り時に、最新の書き込みが反映されていない可能性がある
- 読み取りリクエストはセカンダリノードに行く可能性がある
- コストは低いが、最新のデータが反映されない場合がある
(引用元:Amazon DynamoDB How it works)
整合性レベルを選択する場面は2つあります。
- メインテーブルからデータを読み取る際
- デフォルトは結果整合性
ConsistentRead=True
を指定すれば強い整合性が可能
- セカンダリインデックスを選ぶ際
- ローカルセカンダリインデックスでは強い整合性が可能
- グローバルセカンダリインデックスでは結果整合性のみ
つまり、DynamoDBでは整合性とコストのトレードオフを考慮し、用途に応じて適切な整合性レベルを選択する必要があります。最新データが求められるケースでは強い整合性を、コストを抑えたい場合は結果整合性を選びます。
データの整合性は分散システムにおける重要な概念です。DynamoDBでは柔軟な設定が可能なので、アプリケーションの要件を考慮して運用することが重要になります。
グローバルテーブルとレプリケーション
グローバルテーブル機能を使えば、DynamoDBテーブルを複数のリージョンにレプリケートできます。レプリカ間でデータは自動的に複製され、リージョン障害に強くなります。また、ユーザーはどのレプリカテーブルに対しても読み取りと書き込みが出来る(マルチアクティブ)ため、リージョンに最も近いレプリカにアクセスすることでレイテンシが改善されます。
DynamoDB Transactions
DynamoDB Transactionsでは、複数のアイテムに対する読み取り/書き込みをアトミックに実行できます。トランザクションにより、データの整合性が保たれ、競合状態やデッドロックの問題が回避できます。トランザクションはAWS SDKやAWS CLIから利用可能です。
DynamoDB Transactionsの詳細に仕組みについては、USENIX ATC 2023で公開された論文『Distributed Transactions at Scale in Amazon DynamoDB』がありますので、もっと知りたいという方は是非ご参照ください。
データモデリングのベストプラクティス
DynamoDBのデータモデリングの手順について紹介します。以下のようなステップで行います。
- アプリケーションの理解
- ER図の作成
- データアクセスパターンを特定する
- プライマリキー構造のモデル化
- セカンダリインデックスによる追加アクセスパターンへの対応
アプリケーションの理解
- アプリケーションの機能要件を十分に把握する
- ユーザーの体験を理解する(なるべくフロントに近いレイヤーの理解)
- 開発者だけでなく、サポート、営業など関係者から要件を収集する
- 現在の要件だけでなく、将来的な変更や拡張の可能性も考慮する
ER図の作成
- アプリケーションのデータオブジェクト(エンティティ)を特定する
- エンティティ間の関係性(1対1、1対多、多対多など)を可視化する
- ER図を作成することで、データの構造を明確にする
- 小規模アプリケーションでも、ER図を作成することを推奨
データアクセスパターンを特定する
RDBの場合、通常はER図がそのままデータベースとなります。エンティティがテーブルになり、リレーションシップが外部キーで構成し、将来の柔軟なクエリパターンに対応できるようにデータを設計します。しかし、DynamoDBにおいては将来の柔軟性に基づく設計ではなく、特定のアクセスパターンに対応できるようにデータを設計します。
- API主導アプローチとUI主導アプローチがある
- API主導ならAPIエンドポイントと応答シェイプを列挙する
- UI主導なら画面ごとの必要データを特定する
- アクセスパターンをチャート形式で詳細に記録する
DynamoDBでは、アクセスパターンを前もって設計しておけば、ほとんどのデータモデルを扱うことができます。ユーザが直面する最大の問題は、前もって自分のパターンを考慮することができず、データモデルが固まってから行き詰まることです。
プライマリキー構造のモデル化
- エンティティごとにプライマリキー構造を設計する
- 単純キーと複合キーがあり、要件に合わせて使い分ける
- ユニーク性の確保が最優先、次にアクセスパターンの最適化を図る
- プレフィックス(
CUSTOMER#xxx
,ORDER#xxx
など)を付与してエンティティタイプを識別しやすくする - エンティティチャートにプライマリキー構造をまとめる
セカンダリインデックスによる追加アクセスパターンへの対応
- プライマリキーだけでは実現できないアクセスパターンに対応
- 1つのインデックスで複数のパターンをカバーするよう設計する
- LSIとGSIの違いと整合性モデルを理解する
このようにDynamoDBのデータモデリングは、アクセスパターンを起点に、プライマリキー、セカンダリインデックスを使い分けながら、要件を満たすモデルを作り上げていくプロセスです。
以下のエントリーがDynamoDBのデータモデリングについて非常によくまとまっていてオススメです。
単一テーブル設計の利点と適用場面
単一テーブル設計とは、アプリケーション全体のデータを1つのDynamoDBテーブルにまとめて格納する設計手法のことです。DynamoDBでは単一テーブル設計が推奨されています。これは、従来のRDBとは異なるアプローチです。
単一テーブル設計のメリット
- 1回のリクエストで複数の異種アイテムを取得できるため、パフォーマンスが向上する
- (テーブルとして事前結合しているため)結合操作が不要になり、大規模データでも高速にスケールできる
- アラームやメトリクス等の監視対象数が減るため運用コストが低く、プロビジョニングされた容量を有効活用できる
単一テーブル設計の最大のメリットは、アクセスパターンに合わせてデータを非正規化し、1回のリクエストで必要なデータを取得できることです。RDBの結合操作に比べ、大幅な性能向上が期待できます。
単一テーブル設計の課題
- 正規化されたデータモデルとは発想が異なるため、学習コストが高い
- 新しいアクセスパターンへの対応が難しい場合がある
- 分析用途では再正規化が必要となり、前処理が複雑になる
一方で、単一テーブル設計を適切に実践するには一定の学習コストがかかります。また、要件の変更があった場合の対応が難しくなる可能性があります。分析用途では再度正規化が必要となるなど、運用面での課題もありますので、分析用途には分析向けのAWSサービスの利用を検討するのが良いでしょう。
DynamoDBの操作
基本的なアイテムへの操作
PutItem
: 新しいアイテムを追加する操作GetItem
: 特定のアイテムを取得する操作UpdateItem
: 既存のアイテムを更新する操作DeleteItem
: 特定のアイテムを削除する操作
アイテムベースのアクションには以下の原則があります。
- 完全なプライマリキーをリクエストで指定しなければならない
- すべてのアイテム操作はセカンダリインデックスではなく、メインテーブルに対して実行しなければならない(
Query
やScan
はセカンダリインデックスに対しても可能)
QueryとScanの違い
Query
: パーティションキーが指定された値に等しいアイテムを取得する操作- ソートキーを使用して特定の範囲のアイテムを取得可能
- インデックスを使用して効率的なクエリが可能
- フィルター式を使用して結果をさらに絞り込める
Scan
: テーブル全体をスキャンしてアイテムを取得する操作- フィルター式を使用して条件に一致するアイテムを返す
- 大規模なテーブルに対して使用すると非効率
- スキャン専用のインデックスを使用して効率化が可能
基本的にはQueryを使う場面が多いですが、いくつかの場面ではScanを使用することも検討できます。
- テーブルが極めて小さい
- テーブルのすべてのデータをエクスポートする
- スパースインデックスをScanするほうが効率的な場合
(引用元:Amazon DynamoDB Advanced Design Pattern)
バッチ操作とトランザクション操作
- バッチ操作(
BatchGetItem
、BatchWriteItem
)- 複数のアイテムを一度に取得・追加・削除する操作
- 一括処理によってスループットを節約可能
- 部分的な成功が許容される(一部のアイテムが失敗しても他は成功)
- トランザクション操作(
TransactGetItems
、TransactWriteItems
)- 複数のアイテムに対する読み取り・書き込みをアトミックに実行する操作
- ACID特性を保証(原子性、一貫性、分離性、耐久性)
- 強い整合性が必要な場合に使用
- トランザクションの失敗はすべてロールバックされる
- 消費するキャパシティユニットは通常の2倍になる
パフォーマンスと最適化
パフォーマンスメトリクスについて
- 読み込み/書き込みキャパシティーユニット(RCU/WCU)
- RCU(Read Capacity Unit):
- 4KB単位の強い整合性のある読み取りリクエストの数(1回/秒あたり)
- あるいは、4KB単位の結果整合性のある読み込みリクエストの数(2回/秒あたり)
- WCU(Write Capacity Unit):
- 1KB単位の書き込みリクエストの数(1回/秒あたり)
- RCU(Read Capacity Unit):
- レイテンシ: リクエストの発行からレスポンスの受信までの時間
- スループット: 一定時間内に処理できるリクエストの数
コスト効率の良い読み書きキャパシティーユニットの管理
- プロビジョンドキャパシティーモード
- 読み書きキャパシティーユニットを事前に設定
- 安定したトラフィックパターンに適している
- オンデマンドモード
- 読み書きキャパシティーユニットを自動的にスケーリング
- 予測不可能なトラフィックパターンに適している
キャッシング(DAX:DynamoDB Accelerator)
- DAXの概要
- DynamoDBの読み取りパフォーマンスを向上させるインメモリキャッシュ
- 1桁台のミリ秒単位からマイクロ秒単位までの低レイテンシを実現
- DAXの使用ケース
- 読み取り集中のワークロード
- 同じデータへの繰り返しアクセス
- DAXを使用するアプリケーションを構築する場合、結果整合性であることを許容するように設計する必要がある
セキュリティとアクセス管理
DynamoDBを活用する上で、セキュリティとアクセス管理は非常に重要な課題です。ここでは、IAMによるアクセス制御、暗号化オプション、VPCエンドポイントの活用について説明します。
IAMによるアクセス制御
IAM(Identity and Access Management)は、AWSリソースへのアクセスを安全に制御するサービスです。ユーザー、グループ、ロールを作成し、きめ細かな権限管理を行うことができます。DynamoDBに対しては、読み取り、書き込み、管理操作に対する権限をIAMポリシーで定義し、必要な権限のみを付与することが重要です。また、特定のIPアドレス範囲からのアクセスのみを許可するなど、条件付きアクセスを設定することもできます。
IAMを活用する上では、最小権限の原則に基づいて必要最小限の権限を付与し、定期的にアクセス権限の見直しと監査を行うことが求められます。これにより、セキュリティリスクを最小限に抑えつつ、適切なアクセス管理を実現できます。
暗号化オプション(トランスポートレベルとサーバーサイド)
DynamoDBでは、トランスポートレベルとサーバーサイドの両方で暗号化オプションを提供しています。トランスポートレベルの暗号化は、クライアントとDynamoDBの間の通信を保護するもので、SSL/TLSによるデータの暗号化が行われます。一方、サーバーサイドの暗号化は、保管中のデータを暗号化するもので、AWS KMS(Key Management Service)または独自のキーを使用して実現されます。
DynamoDBとVPCエンドポイントの活用
VPC(Virtual Private Cloud)は、AWSクラウド内の独立したネットワーク環境を提供するサービスです。セキュリティグループとネットワークACLを使用して、きめ細かなアクセス制御が可能です。DynamoDBを使用する際、VPCエンドポイントを活用することで、インターネットを経由せずにDynamoDBにアクセスできます。これにより、VPC内のセキュリティを維持しつつ、DynamoDBとの通信を最適化できます。
設定の際は、エンドポイントポリシーによるアクセス制御や、必要なDNSホスト名の設定に注意が必要です。VPCエンドポイントを適切に活用することで、セキュリティと性能の両方を向上させることができます。
バックアップと復元、ポイントインタイムリカバリー
DynamoDBは、データの耐久性と可用性を確保するために、自動的にデータをレプリケートします。ただし、誤操作やアプリケーションのバグによるデータ損失に備えて、定期的なバックアップを取得することが重要です。
DynamoDBでは、オンデマンドバックアップと継続的バックアップの2種類のバックアップ方式を提供しています。オンデマンドバックアップは、任意のタイミングで手動でバックアップを作成できます。一方、継続的バックアップは、指定した期間の間、自動的にバックアップを作成し、ポイントインタイムリカバリーを可能にします。
ポイントインタイムリカバリーを使用すると、指定した時点の状態にテーブルを復元できます。これにより、誤ってデータを削除や更新してしまった場合でも、簡単にデータを元の状態に戻すことができます。バックアップと復元、ポイントインタイムリカバリーを適切に活用することで、データの可用性と耐久性を確保できます。
最近のDynamoDBに関するアップデート
さいごに、DynamoDBは常に進化し続けており、AWSは定期的に新機能や改善点を導入しています。ここでは、最近のDynamoDBに関するアップデートをピックアップして紹介します。
インターフェイス型VPCエンドポイント(PrivateLink)をサポート
従来DynamoDBはGateway型のVPCエンドポイントしかサポートされていませんでした。そのため、Direct Connect(Private VIF or Tranit VIF)やSite-to-Site VPNの接続元からアクセスする際にはVPC上にプロキシを用意する必要がありました。
Amazon OpenSearch Service のゼロ ETL 統合をサポート
PITR, DynamoDB Streamを利用しAmazon Open Search Serviceへシームレスな同期が可能になりました。
指定した区間のエクスポートができる増分エクスポート機能
従来、テーブル全体をエクスポートする「フルエクスポート」のみが提供されていましたが、指定した期間のみエクスポートする「増分エクスポート」がサポートされました。
削除保護機能
ようやく Amazon DynamoDB でも削除保護が実装されました
以上、『AWS 入門ブログリレー 2024』の37日目のエントリ『Amazon DynamoDB』編でした。
次回、2024/05/08 は弊社 yhana による「AWS IAM Identity Center」の予定です!